/*
 * INCLUDE FILES
 ****************************************************************************************
 */
#include <stdio.h>
#include <string.h>

#include "gap_api.h"
#include "gatt_api.h"
#include "ble_stack.h"

#include "os_timer.h"
#include "os_mem.h"
#include "jump_table.h"
#include "co_printf.h"
#include "co_log.h"
#include "sys_utils.h"

#include "prf_server.h"
#include "prf_client.h"

#include "plf.h"
#include "driver_system.h"
#include "driver_pmu.h"
#include "driver_uart.h"
#include "driver_spi.h"

#undef LOG_LOCAL_LEVEL
#define LOG_LOCAL_LEVEL        (LOG_LEVEL_INFO)
const char *app_tag = "project";


#define SYSTEM_STACK_SIZE           0x800

uint8_t slave_link_conidx;
uint8_t master_link_conidx;
uint8_t tick = 1;

__attribute__((section("stack_section"))) static uint32_t system_stack[SYSTEM_STACK_SIZE/sizeof(uint32_t)];

const struct jump_table_version_t _jump_table_version __attribute__((section("jump_table_3"))) =
{
    .stack_top_address = &system_stack[SYSTEM_STACK_SIZE/sizeof(uint32_t)],
    .firmware_version = 0x00000000,
};

const struct jump_table_image_t _jump_table_image __attribute__((section("jump_table_1"))) =
{
    .image_type = IMAGE_TYPE_APP,
    .image_size = 0x19000,
};


void proj_ble_gap_evt_func(gap_event_t *event)
{
    switch(event->type)
    {
        case GAP_EVT_ADV_END:
        {
            LOG_INFO(app_tag,"adv_end,status:0x%02x\r\n",event->param.adv_end.status);
        }
        break;
        case GAP_EVT_SCAN_END:
            LOG_INFO(app_tag,"scan_end,status:0x%02x\r\n",event->param.scan_end_status);
            break;
        case GAP_EVT_ADV_REPORT:
        {
            if(memcmp(event->param.adv_rpt->src_addr.addr.addr,"\xfd\x37\xe3\xe1\xfC\x02",6)==0)
            {
                LOG_INFO(app_tag,"evt_type:0x%02x,rssi:%d\r\n",event->param.adv_rpt->evt_type,event->param.adv_rpt->rssi);
            }

        }
        break;

        case GAP_EVT_ALL_SVC_ADDED:
        {
            LOG_INFO(app_tag,"all svc added\r\n");
        }
        break;

        case GAP_EVT_MASTER_CONNECT:
        {
            LOG_INFO(app_tag,"master[%d],connect. link_num:%d\r\n",event->param.master_connect.conidx,gap_get_connect_num());
            master_link_conidx = (event->param.master_connect.conidx);
        }
        break;

        case GAP_EVT_SLAVE_CONNECT:
        {
            LOG_INFO(app_tag,"slave[%d],connect. link_num:%d\r\n",event->param.slave_connect.conidx,gap_get_connect_num());
            slave_link_conidx = event->param.slave_connect.conidx;
            gap_conn_param_update(event->param.slave_connect.conidx, 9, 9, 10, 500);
            gap_security_req(event->param.slave_connect.conidx);
        }
        break;

        case GAP_EVT_DISCONNECT:
        {
            //gap_bond_manager_info_clr("\x0C\x0C\x0C\x0C\x0C\x0B", 0);
            LOG_INFO(app_tag,"Link[%d] disconnect,reason:0x%02X\r\n",event->param.disconnect.conidx
                     ,event->param.disconnect.reason);
            gap_start_advertising(0);
        }
        break;

        case GAP_EVT_LINK_PARAM_REJECT:
            LOG_INFO(app_tag,"Link[%d]param reject,status:0x%02x\r\n"
                     ,event->param.link_reject.conidx,event->param.link_reject.status);
            break;

        case GAP_EVT_LINK_PARAM_UPDATE:
            LOG_INFO(app_tag,"Link[%d]param update,interval:%d,latency:%d,timeout:%d\r\n",event->param.link_update.conidx
                     ,event->param.link_update.con_interval,event->param.link_update.con_latency,event->param.link_update.sup_to);
            break;

        case GAP_EVT_CONN_END:
            LOG_INFO(app_tag,"conn_end,reason:0x%02x\r\n",event->param.conn_end_reason);
            break;

        case GAP_EVT_PEER_FEATURE:
            LOG_INFO(app_tag,"peer[%d] feats ind\r\n",event->param.peer_feature.conidx);
            break;

        case GAP_EVT_MTU:
            LOG_INFO(app_tag,"mtu update,conidx=%d,mtu=%d\r\n"
                     ,event->param.mtu.conidx,event->param.mtu.value);
            break;
        case GAP_EVT_LINK_RSSI:
            LOG_INFO(app_tag,"link rssi %d\r\n",event->param.link_rssi);
            break;
        default:
            break;
    }
}
void app_rtos_entry(uint8_t type, void *arg);

__attribute__((section("ram_code"))) void user_entry_before_sleep_imp(void)
{
    pmu_calibration_stop();
    uart_putc_noint_no_wait(UART0, 's');
}

__attribute__((section("ram_code"))) void user_entry_after_sleep_imp(void)
{
    log_init();
    uart_putc_noint_no_wait(UART0, 'w');

    NVIC_EnableIRQ(PMU_IRQn);
    pmu_calibration_start(PMU_CALI_SEL_RCLFOSC, LP_RC_CALIB_CNT);
}
os_timer_t ttt_timer;
void ttt_tim_fn(void *arg)
{
    LOG_INFO(app_tag,"ttt\r\n");
}
void proj_init(void)
{
    LOG_INFO(app_tag,"proj_init\r\n");

    LOG_INFO(app_tag,"lp clk=%d\r\n", pmu_get_rc_clk(false));

    gap_dev_name_set("FR8010H", strlen("FR8010H"));

    //gap_set_dev_name("FR8010H", strlen("FR8010H"));
    /* register GAP callback function*/
    gap_set_cb_func(proj_ble_gap_evt_func);
    /* initialize SMP */
    //gap_bond_manager_init(BLE_BONDING_INFO_SAVE_ADDR, BLE_REMOTE_SERVICE_SAVE_ADDR, 8, true);
    gap_bond_manager_init(BLE_BONDING_INFO_SAVE_ADDR,BLE_REMOTE_SERVICE_SAVE_ADDR,8,true);

    uint8_t adv_data[]="\x09\x08\x46\x52\x38\x30\x31\x30\x46\x00";
    uint8_t rsp_data[]="\x09\xFF\x00\x60\x52\x57\x2D\x42\x4C\x45";

    gap_adv_param_t adv_param;
    adv_param.adv_mode = GAP_ADV_MODE_UNDIRECT;
    adv_param.adv_addr_type = GAP_ADDR_TYPE_PRIVATE;
    adv_param.adv_chnl_map = GAP_ADV_CHAN_ALL;
    adv_param.adv_filt_policy = GAP_ADV_ALLOW_SCAN_ANY_CON_ANY;
    adv_param.adv_intv_min = 600;
    adv_param.adv_intv_max = 600;
    gap_set_advertising_param(&adv_param);
    gap_set_advertising_data(adv_data, sizeof(adv_data)-1);
    gap_set_advertising_rsp_data(rsp_data, sizeof(rsp_data)-1);
    gap_start_advertising(0);

    prf_server_create();
    prf_client_create();

    os_timer_init(&ttt_timer,ttt_tim_fn,NULL);
    os_timer_start(&ttt_timer,2000,1);

    NVIC_EnableIRQ(PMU_IRQn);
}
#include "rtthread.h"
static rt_thread_t task1_id;
static struct rt_timer RT_timer;
void RT_tim_fn(void *parameter)
{
    co_printf("rt_tim\r\n");
}
void vTask2(void *parameter)
{
    while (1)
    {
        LOG_INFO(app_tag,"vTask2\r\n");
        rt_thread_mdelay(1000);
    }
}
void vTask1(void *parameter)
{
    proj_init();

    rt_timer_init(&RT_timer,"timer1",RT_tim_fn,RT_NULL,200,RT_TIMER_FLAG_SOFT_TIMER|RT_TIMER_FLAG_PERIODIC);
    rt_timer_start(&RT_timer);
    //while (1)
    {
        LOG_INFO(app_tag,"vTask1\r\n");
        //rt_thread_mdelay(1000);
    }
}
static void task1_cleanup(struct rt_thread *tid)
{
    if (tid == task1_id)
    {
        LOG_INFO(app_tag,"vtasl1 end\r\n");
        task1_id = RT_NULL;
    }
}


void user_main(void)
{
    /* initialize log module */
    log_init();

    /* initialize PMU module at the beginning of this program */
    pmu_sub_init();

    /* set system clock */
    system_set_clock(SYSTEM_CLOCK_SEL);

    /* set local BLE address */
    mac_addr_t mac_addr;
    mac_addr.addr[0] = 0xbd;
    mac_addr.addr[1] = 0xad;
    mac_addr.addr[2] = 0x10;
    mac_addr.addr[3] = 0x11;
    mac_addr.addr[4] = 0x20;
    mac_addr.addr[5] = 0x20;
    gap_address_set(&mac_addr, BLE_ADDR_TYPE_PUBLIC);   //BLE_ADDR_TYPE_RANDOM_RESOVABLE

    /* configure ble stack capabilities */
    ble_stack_configure(BLE_STACK_ENABLE_MESH,
                        BLE_STACK_ENABLE_CONNECTIONS,
                        BLE_STACK_RX_BUFFER_CNT,
                        BLE_STACK_RX_BUFFER_SIZE,
                        BLE_STACK_TX_BUFFER_CNT,
                        BLE_STACK_TX_BUFFER_SIZE,
                        BLE_STACK_ADV_BUFFER_SIZE,
                        BLE_STACK_RETENTION_RAM_SIZE,
                        BLE_STACK_KEY_STORAGE_OFFSET);
    //jump_table_set_static_keys_store_offset(JUMP_TABLE_STATIC_KEY_OFFSET);
    /* initialize ble stack */
    ble_stack_init();

#if 1
    //system_sleep_disable();
    system_sleep_enable();
#endif

    __jump_table.system_option |= SYSTEM_OPTION_ENABLE_RTOS;
    rtos_entry = app_rtos_entry;
    if(__jump_table.system_option & SYSTEM_OPTION_ENABLE_RTOS)
    {
        GLOBAL_INT_STOP();
        rtthread_init();

        task1_id = rt_thread_create("main", vTask1, RT_NULL,1024, 1, 20);
        RT_ASSERT(task1_id != RT_NULL);
        task1_id->cleanup = task1_cleanup;
        rt_thread_startup(task1_id);

        rt_thread_t task2_id;
        task2_id = rt_thread_create("task2", vTask2, RT_NULL,1024, 3, 20);
        rt_thread_startup(task2_id);

        rtos_entry(RTOS_ENTRY_TYPE_INIT, NULL);
        rt_system_scheduler_start();
        LOG_ERR(app_tag,"can't go here\r\n");
    }
    while(1);
}


